package com.bobo.mm.socket.provider;

import android.os.Handler;
import android.os.HandlerThread;
import android.util.Log;

import com.bobo.mm.ClientConfig;
import com.bobo.mm.socket.InnerSocketListener;
import com.bobo.mm.socket.decoder.IDecoder;
import com.bobo.mm.util.HexDump;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;

/**
 * 阻塞I/O socket 提供者
 * Created by cocolove2 on 2020/8/11.
 */
public class BlockSocketClient extends BaseSocketClient {
    private static final String TAG = "TcpBlockSocketClient";

    private Socket mClient;
    /**
     * 消息读取
     */
    private BufferedInputStream mReader;
    /**
     * 消息发送
     */
    private PrintStream mWriter;
    /**
     * 主socket线程
     */
    private HandlerThread mSocketThread;
    private Handler mSocketHandler;
    /**
     * 写线程
     */
    private HandlerThread mWriterThread;
    private Handler mWriterHandler;
    private final String clientId;
    private final IDecoder mDecoder;
    /**
     * 读数据缓存
     */
    private final ByteBuf mReadByteBuffer;
    /**
     * 完整数据帧缓存
     */
    private final List<byte[]> mFrameList = new ArrayList<>();

    public BlockSocketClient(ClientConfig clientConfig, InnerSocketListener listener, IDecoder frameHander) {
        super(clientConfig, listener);
        if (frameHander == null) {
            throw new IllegalArgumentException("AFrameHandler不能为空");
        }
        mDecoder = frameHander;
        this.clientId = clientConfig.getClientId();
        mReadByteBuffer = Unpooled.buffer(8092);
    }


    private void realCreateSocket(String ip, int port) {
        try {
            mClient = new Socket(ip, port);
            mClient.setKeepAlive(true);
            setClientConnState(CONNECTING);
            mReader = new BufferedInputStream(mClient.getInputStream());
            mWriter = new PrintStream(mClient.getOutputStream());
            Log.d(TAG, "socket 创建成功--" + mClient.getInetAddress() + "#" + mClient.getPort());
            createWriterThread();
            setClientConnState(CONNECTED);
            stopReconHandler();
            getSocketListener().onOpen(clientId, null);
            loopReader();
        } catch (IOException e) {
            mSocketThread = null;
            Log.e(TAG, "loopReader=>" + e.getMessage());
            try {
                if (mClient != null) {
                    mClient.close();
                    mClient = null;
                }
            } catch (IOException e1) {
                e1.printStackTrace();
            }
            failError(e);
        }
    }


    /**
     * 创建写线程
     */
    private void createWriterThread() {
        if (mWriterThread == null) {
            mWriterThread = new HandlerThread("writer-thread");
            mWriterThread.start();
        }
        mWriterHandler = new Handler(mWriterThread.getLooper());
    }


    private void loopReader() throws IOException {
        byte[] buffer = new byte[READER_BUFFER_SIZE];
        int readLen = -1;
        while (!mSocketThread.isInterrupted()) {
            if (isConnected() && !mClient.isInputShutdown()) {
                readLen = mReader.read(buffer, 0, READER_BUFFER_SIZE);
                if (readLen != -1) {
                    //接收数据写入缓存
                    mReadByteBuffer.writeBytes(Arrays.copyOf(buffer, readLen));
                    //数据帧解码
                    mDecoder.decode(clientId, mReadByteBuffer, mFrameList);
                    //分发数据帧
                    for (byte[] item : mFrameList) {
                        getSocketListener().onMessage(clientId, item);
                    }
                } else {
                    Log.e(TAG, "socket 读线程关闭:readlen=" + readLen);
                    failError(new IOException("socket is error can not read"));
                    break;
                }
            } else {
                failError(new IOException("socket is error can not read"));
            }
        }
    }

    @Override
    public void startConnect() {
        if (mSocketThread == null) {
            mSocketThread = new HandlerThread("socket-thread");
            mSocketThread.start();
            mSocketHandler = new Handler(mSocketThread.getLooper());
        }
        mSocketHandler.post(new Runnable() {
            @Override
            public void run() {
                String[] arr = getClientConfig().getSocketUrl().split(":");
                realCreateSocket(arr[0], Integer.parseInt(arr[1]));
                BlockSocketClient.super.startConnect();
            }
        });

    }


    @Override
    public void stopConnect() {
        super.stopConnect();
        try {
            if (mClient != null) {
                mClient.close();
                mClient = null;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        if (mSocketThread != null) {
            mSocketThread.quitSafely();
            mSocketHandler = null;
            mSocketThread = null;
        }

        if (mWriterThread != null) {
            mWriterThread.quitSafely();
            mWriterHandler = null;
            mWriterThread = null;
        }
        mReadByteBuffer.clear();
        getSocketListener().onClosed(clientId, 0, "ok");
    }

    @Override
    public boolean sendMessage(final String msg) {
        if (isCanWrite()) {
            mWriterHandler.post(new Runnable() {
                @Override
                public void run() {
                    realSendMessage(msg.getBytes());
                }
            });
            return true;
        } else {
            return false;
        }
    }

    private void realSendMessage(byte[] frame) {
        try {
            if (frame != null) {
                Log.i(TAG, "发送的数据=====>" + frame.length + "#" + HexDump.dumpHexString(frame));
                mWriter.write(frame);
                mWriter.flush();
            } else {
                Log.d(TAG, "发送的数据为空，不处理");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public boolean sendMessage(final byte[] data) {
        if (isCanWrite()) {
            mWriterHandler.post(new Runnable() {
                @Override
                public void run() {
                    realSendMessage(data);
                }
            });
            return true;
        } else {
            return false;
        }
    }

    @Override
    public boolean isConnected() {
        return mClient != null && mClient.isConnected() && !mClient.isClosed();
    }

    @Override
    void reconnect(String clientId, int reconCount) {
        setClientConnState(RECONNECT);
        getSocketListener().onReconnecting(getClientConfig().getClientId(), reconCount);
        startConnect();
    }

    private boolean isCanWrite() {
        return isConnected() && !mClient.isOutputShutdown() && mWriterThread != null && mWriterThread.isAlive() && mWriterHandler != null;
    }
}
